using UnityEngine;
using System.Collections;
using UnityEditor;
using System.Collections.Generic;
using XML;
using System.IO;
using System.Text;


public class CurveCreationWindow : EditorWindow {

    int indexCurve = 0;
    int indexPoint = 0;

    private List<EnemyCurve> _curves = new List<EnemyCurve>();
    private EnemyCurve _selectedCurve;
    private GameObject _selectedPoint;

    private GameObject baseTile;

    private string nameLevel = "";
    private string nameLevelNext = "";
    private string nameDecors = "";
    private float _timeLifeMolecule;
	private float _timeStar3 = 0.0f;
	private float _timeStar2 = 0.0f;
	private float _timeStar1 = 0.0f;
	
    [MenuItem("Scary/Curves Creations")]
    static void Init()
    {
        // Get existing open window or if none, make a new one:
        CurveCreationWindow window = (CurveCreationWindow)EditorWindow.GetWindow(typeof(CurveCreationWindow));
        window.Repaint();


    }
    

    void OnGUI()
    {

        //Select Curve
        CurveSelection();

        //Point Curve Selection
        PointCurveSelection();

        EditorGUILayout.Space();
        EditorGUILayout.Space();

        //Create Curve
        CurveCreation();

        //Delete Curve
        ButtonDeleteCurve();

        //Delete All Curves
        DeleteAll();

        //Create Point
        PointCurveCreate();

        //Delete Point of curve
        DeletePointCurve();

        EditorGUILayout.Space();
        EditorGUILayout.Space();

        //Draw Info of the current Curve
        InfoCurve();

        EditorGUILayout.Space();
        EditorGUILayout.Space();

        EditorGUILayout.Space();
        EditorGUILayout.Space();
		
		_timeStar3 = EditorGUILayout.FloatField("Time 3 Star : ", _timeStar3);
		_timeStar2 = EditorGUILayout.FloatField("Time 2 Star : ", _timeStar2);
		_timeStar1 = EditorGUILayout.FloatField("Time 1 Star : ", _timeStar1);
		
		EditorGUILayout.Space();
        EditorGUILayout.Space();

        SaveLevel();

        EditorGUILayout.Space();

        LoadLevel();

        EditorGUILayout.Space();
        EditorGUILayout.Space();

        nameLevelNext = EditorGUILayout.TextField("Next Level : ", nameLevelNext);


        EditorGUILayout.BeginHorizontal();

        nameDecors = EditorGUILayout.TextField("Decors : ", nameDecors);
        if (GUILayout.Button("Load Decors"))
        {
            DeserializeDecors(nameDecors);
        }

        if (GUILayout.Button("Delete Decors"))
        {
            DestroyImmediate(GameObject.Find("Tiles"));
        }
        EditorGUILayout.EndHorizontal();

        //Draw Debug Curve
        DrawCurves();
    }

#region CurvesOperations

    //Curve Selection
    void CurveSelection()
    {
        //if (_curves.Count > 0)
        {
            string[] nameCurves = new string[_curves.Count];
            int index = 0;
            foreach (EnemyCurve curve in _curves)
            {
                if (_selectedCurve == curve)
                    indexCurve = index;

                nameCurves[index++] = curve.name;
            }
            indexCurve = EditorGUILayout.Popup(indexCurve, nameCurves);

            if (_curves.Count > 0)
            {
                _selectedCurve = _curves[indexCurve];
            } 
                
        }
    }

    //Point Selection
    void PointCurveSelection()
    {
        if (_selectedCurve == null)
        {
            string[] empty = new string[0];
            indexPoint = EditorGUILayout.Popup(indexPoint, empty);
            return;
        }            

        string[] namePoint = new string[_selectedCurve.transform.GetChildCount()];
        int index = 0;
        int nbChild = _selectedCurve.transform.GetChildCount();

        for (int i = 0; i < nbChild; ++i)
        {
            if (_selectedCurve.transform.GetChild(i).name == _selectedPoint.name)
            {
                indexPoint = index;
            }

            namePoint[index++] = _selectedCurve.transform.GetChild(i).name;
        }

        indexPoint = EditorGUILayout.Popup(indexPoint, namePoint);

        if (_curves.Count > 0 && nbChild != 0)
        {
            _selectedPoint = _selectedCurve.transform.GetChild(indexPoint).gameObject;
            Selection.activeGameObject = _selectedPoint;
        }
    }

    //Create a new curve with first curve point
    void CurveCreation()
    {
        if (GUILayout.Button("Create Curve"))
        {
            GameObject newCurve = new GameObject();

            int numPoint = 0;
            int nbChild = _curves.Count;
            for (int i = 0; i < nbChild; ++i)
            {
                int num = int.Parse(_curves[i].name.Substring(_curves[i].name.Length - 2));
                if (num > numPoint)
                {
                    numPoint = num + 1;
                }
            }

            //Get name of curve
            string name = "Curve";


            if (numPoint < 10)
                name += "0";

            if (numPoint < 100)
                name += "0";

            newCurve.name = name + _curves.Count;

            newCurve.transform.position = Vector3.zero;
            newCurve.AddComponent(typeof(EnemyCurve));

            //Set the selected Curve
            _selectedCurve = newCurve.GetComponent<EnemyCurve>();

            _curves.Add(_selectedCurve);

            //Create first point
            GameObject newPoint = new GameObject();
            newPoint.name = newCurve.transform.GetChildCount().ToString();
            newPoint.transform.parent = newCurve.transform;

            

            //Select point
            GetSelectedPoint();
            

            Debug.Log("Curve Create");
        }
    }

    //Delete a curve
    void ButtonDeleteCurve()
    {
        if (GUILayout.Button("Delete current Curve"))
        {

            DeleteCurve(_selectedCurve);            

            if (_curves.Count != 0)
            {
                _selectedCurve = _curves[0];
                GetSelectedPoint();
            }
        }
        
    }

    void DeleteCurve(EnemyCurve curve)
    {
        if (curve != null)
        {
            _curves.Remove(curve);
            DestroyImmediate(curve.gameObject);
        }
    }

    //Delete all curves
    void DeleteAll(bool force = false)
    {
        if (GUILayout.Button("Delete all Curves") || force)
        {
            List<EnemyCurve> temp = _curves;

            foreach (EnemyCurve curve in temp)
            {
                DestroyImmediate(curve.gameObject);
            }
            _curves.Clear();
            _selectedCurve = null;
            _selectedPoint = null;
        }
    }

    //Create a point of curve for selected curve
    void PointCurveCreate()
    {
        if (GUILayout.Button("Create Point for the curve"))
        {
            //Create first point
            GameObject newPoint = new GameObject();

            int numPoint = 0;
            int nbChild = _selectedCurve.transform.GetChildCount();
            for (int i = 0; i < nbChild; ++i)
            {
                int num = int.Parse(_selectedCurve.transform.GetChild(i).name);
                if (num >= numPoint)
                {
                    numPoint = num + 1;
                }

            }

            newPoint.name = numPoint.ToString();
            newPoint.transform.parent = _selectedCurve.transform;

            //Select point
            GetSelectedPoint();

            Debug.Log("Curve Point Create");
        }
    }

    //Delete a point of curve
    void DeletePointCurve()
    {
        if (GUILayout.Button("Delete current Point Curve"))
        {
            if (_selectedPoint != null)
            {
                DestroyImmediate(_selectedPoint);
            }


            if (_selectedCurve.transform.GetChildCount() == 0)
            {
                DeleteCurve(_selectedCurve);
            }
            else
                GetSelectedPoint();
        }
    }

    //Get First Point of selected curve
    void GetSelectedPoint()
    {
        
        if (_selectedCurve.transform.GetChildCount() != 0)
        {
            _selectedPoint = _selectedCurve.transform.GetChild(_selectedCurve.transform.GetChildCount() -1).gameObject;
            Selection.activeGameObject = _selectedPoint;
        }
        else
            _selectedPoint = null;
         
    }

#endregion CurvesOperations

    //Load Level
    void DeserializeDecors(string name)
    {
        TextAsset txt = (TextAsset)Resources.Load("XML/" + name);
        InStream stream = new InStream(txt.text);

        baseTile = Resources.Load("Prefabs/baseTile") as GameObject;

        GameObject go = new GameObject();
        go.name = "Tiles";
        go.transform.position = Vector3.zero;

        string type = "";
        Vector3 pos;

        stream.List("Tile", delegate(InStream stream2)
        {
            stream2.Content("Type", out type);
            stream2.Content("pos", out pos);

            GameObject newTile = Instantiate(baseTile) as GameObject;
            newTile.transform.position = pos;
            newTile.transform.parent = go.transform;

            if (type == Const.TYPE_TILE.BUSH.ToString())
            {
                newTile.transform.position = new Vector3(pos.x, -2, pos.z);
                newTile.GetComponent<Tile>().SetType(Const.TYPE_TILE.BUSH);
            }
            else
            {
                newTile.transform.position = new Vector3(pos.x, -5, pos.z);
                newTile.layer = Const.LAYER_GROUND;
            }

        });
    }

#region CurveType

    //Info of ennemys and pop
    void InfoCurve()
    {
        if (_selectedCurve == null)
            return;

        //Type Enemy
        _selectedCurve.typeEnnemyCurve = (Const.ENNEMY_TYPE) EditorGUILayout.EnumPopup(_selectedCurve.typeEnnemyCurve);

        //Nb Enemy
        _selectedCurve.nbEnemyPop = EditorGUILayout.IntField("Nb Enemys : ", _selectedCurve.nbEnemyPop);

        //Enemy Curve duration
        _selectedCurve.durationCurve = EditorGUILayout.FloatField("Duration curve : ", _selectedCurve.durationCurve);
    }


#endregion CurveType

    void SaveLevel()
    {
        EditorGUILayout.BeginHorizontal();

        nameLevel = EditorGUILayout.TextField("Level name : ", nameLevel);

        if (GUILayout.Button("Save"))
        {
            Serialize(nameLevel);
        }

        EditorGUILayout.EndHorizontal();
    }

    void LoadLevel()
    {

        EditorGUILayout.BeginHorizontal();

        

        if (GUILayout.Button("Load"))
        {
            _selectedCurve = null;
            _selectedPoint = null;

            if (_curves.Count != 0)
                DeleteAll(true);            

            Deserialize(nameLevel);
        }

        EditorGUILayout.EndHorizontal();
    }

    //Draw all curves in debug mode
    void DrawCurves()
    {
        foreach (EnemyCurve curve in _curves)
        {
            int nbChild = curve.transform.GetChildCount();

            Color colorCurve = Color.white;
            if (curve == _selectedCurve)
                colorCurve = Color.red;

            if(nbChild == 0 || nbChild == 1)
                continue;
            //Simple Line
            else if(nbChild == 2)
            {
                Debug.DrawLine(curve.transform.GetChild(0).position, curve.transform.GetChild(1).position, colorCurve);
                
            }
            //BSpline
            else
            {
                Transform[] points = new Transform[nbChild + 1];
                for(int i = 0; i < nbChild; ++i)
                {
                    //Debug.DrawLine(curve.transform.GetChild(i).position, curve.transform.GetChild(i+1).position, colorCurve);
                    points[i] = curve.transform.GetChild(i);
                }
                //Debug.DrawLine(curve.transform.GetChild(nbChild - 1).position, curve.transform.GetChild(0).position, colorCurve);
                points[nbChild] = points[0];

                Vector3[] tab = SplineCurve.GetTabPos(points, colorCurve);

                int nbNode = tab.Length;
                for (int i = 0; i < nbNode - 1; ++i)
                {
                    Debug.DrawLine(tab[i], tab[i + 1], colorCurve);
                }
            }

        }
        Repaint();
    }

#region save/load

    //Serialize
    void Serialize(string name)
    {
        OutStream stream = new OutStream();

        stream.Prolog("utf-8")
            .Start("Level")
        .Content("Duration", _timeLifeMolecule);
        stream.Content("NextLevel", nameLevelNext);
		stream.Content("TimeStar3", _timeStar3);
		stream.Content("TimeStar2", _timeStar2);
		stream.Content("TimeStar1", _timeStar1);

        foreach (EnemyCurve curve in _curves)
        {
            stream.Start("Curve")
                .Attribute("Name", curve.name)
                .Attribute("Type", curve.typeEnnemyCurve.ToString())
                .Attribute("Speed", curve.durationCurve)
                .Attribute("NbPop", curve.nbEnemyPop);

            int nbChild = curve.transform.GetChildCount();
            for (int i = 0; i < nbChild; ++i)
            {
                stream.Start("Point")
                    .Attribute("Name", curve.transform.GetChild(i).name)
                    .Content("Position", curve.transform.GetChild(i).position)
                    .End();
            }

                stream.End();
        }

        stream.End();

        //Save in File
        using (FileStream fs = File.Create("./Assets/Resources/XML/" + name + ".xml"))
        {
            AddText(fs, stream.Serialize(true));
        }
    }

    private static void AddText(FileStream fs, string value)
    {
        byte[] info = new UTF8Encoding(true).GetBytes(value);
        fs.Write(info, 0, info.Length);
    }

    //Deserialize 
    void Deserialize(string name)
    {
        TextAsset txt = (TextAsset)Resources.Load("XML/" + name);
        InStream stream = new InStream(txt.text);

        string type = "";
        string nameCurve;
        float speed;
        int nbPop;
        Vector3 pos;

        stream.Content("Duration", out _timeLifeMolecule);
        stream.Content("NextLevel", out nameLevelNext);

        stream.List("Curve", delegate(InStream stream2)
        {
            stream2.Attribute("Type", out type)
                .Attribute("Name", out nameCurve)
                .Attribute("Speed", out speed)
                .Attribute("NbPop", out nbPop);
                

            //Creation of the curve
            GameObject curve = new GameObject();
            curve.name = nameCurve;
            curve.transform.position = Vector3.zero;

            EnemyCurve enemyCurve = curve.AddComponent(typeof(EnemyCurve)) as EnemyCurve;
            enemyCurve.nbEnemyPop = nbPop;
            enemyCurve.durationCurve = speed;
            enemyCurve.SetTypeByName(type);

            _curves.Add(enemyCurve);
            _selectedCurve = enemyCurve;

            stream2.List("Point", delegate(InStream stream3)
            {
                //Creation of all points
                stream3.Attribute("Name", out nameCurve)
                    .Content("Position", out pos);

                GameObject point = new GameObject();
                point.transform.parent = curve.transform;
                point.name = nameCurve;
                point.transform.position = pos;
                _selectedPoint = point;
            });
        });


    }

#endregion save/load

}
